enum CollisionType { WALL_SPHERE, SPHERE_SPHERE,SPHERE_SLAB };

struct FirstCollidingPair
{
	CollisionType collision_type;
	GLint first_index,second_index;
	GLfloat time_to_collide;

};
class Collision
{
public:
	static vector< FirstCollidingPair > get_FirstCollidingPairs(GLfloat time_difference)
	{
		GLfloat min_time_to_collide = time_difference,time_to_collide;
		FirstCollidingPair pair;
		vector< FirstCollidingPair > first_colliding_pairs;

		for(int i=0; i< w_pointer->no_walls; i++){
			for(int j=0; j< w_pointer->no_spheres; j++){
				time_to_collide = get_collision_time(w_pointer->walls[i], w_pointer->spheres[j], time_difference);
				if(time_to_collide == -1)
					continue;
				if(time_to_collide < min_time_to_collide){

					first_colliding_pairs.clear();

					pair.collision_type = WALL_SPHERE; 
					pair.first_index = i;
					pair.second_index = j;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}
				else if(time_to_collide == min_time_to_collide){

					pair.collision_type = WALL_SPHERE; 
					pair.first_index = i;
					pair.second_index = j;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}

			}
		} // WALL-SPHERE checking is over

		for(int i=0; i<w_pointer->no_spheres;i++)
			for(int j=i+1; j<w_pointer->no_spheres;j++){
				//cout<<"i="<<i<<", j="<<j<<"\n";
				time_to_collide = get_collision_time(w_pointer->spheres[i], w_pointer->spheres[j], time_difference);
				if(time_to_collide == -1)
					continue;
				if(time_to_collide < min_time_to_collide){

					first_colliding_pairs.clear();

					pair.collision_type = SPHERE_SPHERE; 
					pair.first_index = i;
					pair.second_index = j;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}
				else if(time_to_collide == min_time_to_collide){

					pair.collision_type = SPHERE_SPHERE; 
					pair.first_index = i;
					pair.second_index = j;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}
			}//SPHERE-SPHERE checking is over
			for(int i=0;i<w_pointer->no_spheres;i++){
				time_to_collide = get_collision_time(w_pointer->spheres[i],*player,time_difference);
				if(time_to_collide == -1)
					continue;
				if(time_to_collide < min_time_to_collide){

					first_colliding_pairs.clear();

					pair.collision_type = SPHERE_SLAB; 
					pair.first_index = i;
					pair.second_index = 0;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}
				else if(time_to_collide == min_time_to_collide){

					pair.collision_type = SPHERE_SLAB; 
					pair.first_index = i;
					pair.second_index = 0;
					pair.time_to_collide = time_to_collide;
					first_colliding_pairs.push_back(pair);
				}				
			} // Player Sphere collision is over
		return first_colliding_pairs;
	}
	static GLfloat get_collision_time(Sphere sphere, Slab slab, GLfloat time_to_collide){
		GLfloat time_slab_sphere;
		Wall temp_wall(Point3D(0,0,0),Point3D(0,1,0),Point3D(0,0,1));
		time_slab_sphere = get_collision_time(temp_wall,sphere,time_to_collide);
		if(time_slab_sphere==-1)
			return -1;

		Point3D c=slab.get_center();
		if(sphere.get_center().y <= c.y+1 && sphere.get_center().y >= c.y-1)
			if(sphere.get_center().z <= c.z+1 && sphere.get_center().z >= c.z-1)
				return time_slab_sphere;
	}
	static GLfloat get_collision_time(Sphere sphere1, Sphere sphere2, GLfloat time_to_collide)
	{

		GLfloat prev_center_to_center_distance = (sphere2.get_center() - sphere1.get_center()).get_magnitude();
		prev_center_to_center_distance = prev_center_to_center_distance < 0 ? -prev_center_to_center_distance : prev_center_to_center_distance;

		sphere1.move(time_to_collide);
		sphere2.move(time_to_collide);

		GLfloat current_center_to_center_distance =  (sphere2.get_center() - sphere1.get_center()).get_magnitude();
		current_center_to_center_distance = current_center_to_center_distance < 0 ? -current_center_to_center_distance : current_center_to_center_distance;

		if(current_center_to_center_distance > sphere1.get_radius() + sphere2.get_radius())
			return -1;

		GLfloat relative_velocity_of_centers = ( prev_center_to_center_distance - current_center_to_center_distance )/time_to_collide;
		GLfloat returning_val = (prev_center_to_center_distance - (sphere1.get_radius()+sphere2.get_radius()))/relative_velocity_of_centers;
		if(returning_val >=-0.0001 && returning_val <=0.0001)
			return -1;
		return((prev_center_to_center_distance - (sphere1.get_radius()+sphere2.get_radius()))/relative_velocity_of_centers);
	}
	static GLfloat get_collision_time(Wall wall, Sphere sphere, GLfloat time_to_collide)
	{
		//if(time_to_collide < 0.01)
		//	return -1;
		GLint sign_factor = 1;
		GLfloat prev_perpendicular_distance = (wall.a*sphere.get_center().x + wall.b*sphere.get_center().y + wall.c*sphere.get_center().z + wall.d)/sqrt(wall.a*wall.a + wall.b*wall.b + wall.c*wall.c);
		if(prev_perpendicular_distance < 0)
			sign_factor*=-1;
		prev_perpendicular_distance = prev_perpendicular_distance < 0 ? -prev_perpendicular_distance : prev_perpendicular_distance;
		//cout<<"prev center "<<sphere.get_center().x<<":"<<sphere.get_center().y<<":"<<sphere.get_center().z<<"\n";
		//cout<<"prev perpendicular_distance "<<prev_perpendicular_distance<<"\n";
		//cout<<"time_to_collide "<<time_to_collide<<"\n";
		sphere.move(time_to_collide);
		GLfloat perpendicular_distance = (wall.a*sphere.get_center().x + wall.b*sphere.get_center().y + wall.c*sphere.get_center().z + wall.d)/sqrt(wall.a*wall.a + wall.b*wall.b + wall.c*wall.c);
		if(perpendicular_distance < 0)
			sign_factor*=-1;
		perpendicular_distance = perpendicular_distance < 0 ? -perpendicular_distance : perpendicular_distance;
		//cout<<"current center "<<sphere.get_center().x<<":"<<sphere.get_center().y<<":"<<sphere.get_center().z<<"\n";
		//cout<<"current perpendicular_distance"<<perpendicular_distance<<"\n";
		//cout<<"sign "<<sign_factor<<"\n\n";

		GLfloat relative_velocity = (prev_perpendicular_distance - sign_factor*perpendicular_distance)/time_to_collide;
		if((perpendicular_distance > sphere.get_radius()) && sign_factor==1)
			return -1;
		//if(sign_factor==-1){
			//cout<<"prev perpendicular_distance "<<prev_perpendicular_distance<<"\n";
			//cout<<"time_to_collide "<<time_to_collide<<"\n";

			//cout<<"current center "<<sphere.get_center().x<<":"<<sphere.get_center().y<<":"<<sphere.get_center().z<<"\n";
			//cout<<"current perpendicular_distance"<<perpendicular_distance<<"\n";

		//	sleep(0.2);
		//}
		return ((prev_perpendicular_distance - sphere.get_radius()) / relative_velocity);
	}
	static void collision_response(Sphere &sphere, Slab &slab)
	{
		Vector3D vel = sphere.get_velocity();
		vel.x= -vel.x;
		sphere.set_velocity(vel);
	}
	static void collision_response(Sphere &sphere1, Sphere &sphere2)
	{
		int sign_factor1;
		int sign_factor2;
		Point3D c1= sphere1.get_center();
		Point3D c2= sphere2.get_center();
		Vector3D normal = c2 - c1; //C1 -> C2

		normal.normalize();
		Vector3D u_vector_1 = sphere1.get_velocity();
		Vector3D velocity_normal_component_1 = normal * ( normal.x*u_vector_1.x + normal.y*u_vector_1.y + normal.z*u_vector_1.z );
		Vector3D velocity_tangent_component_1 = u_vector_1 - velocity_normal_component_1 ;

		sign_factor1 = normal.x*u_vector_1.x + normal.y*u_vector_1.y + normal.z*u_vector_1.z < 0 ? -1 : 1;

		Vector3D u_vector_2 = sphere2.get_velocity();
		Vector3D velocity_normal_component_2 = normal * ( normal.x*u_vector_2.x + normal.y*u_vector_2.y + normal.z*u_vector_2.z );
		Vector3D velocity_tangent_component_2 = u_vector_2 - velocity_normal_component_2 ;

		sign_factor2 = normal.x*u_vector_2.x + normal.y*u_vector_2.y + normal.z*u_vector_2.z < 0 ? -1 : 1;

		GLfloat u1,u2,v1,v2,m1,m2;
		u1 = sign_factor1* velocity_normal_component_1.get_magnitude();
		u2 = sign_factor2* velocity_normal_component_2.get_magnitude();
		//cout<<"U "<<u1<<","<<u2<<"\n";
		m1 = sphere1.get_mass();
		m2 = sphere2.get_mass();

		//m1=300;
		//m2=200;

		v1 = (u1*(m1 - m2) + 2*m2*u2 )/(m1+m2);
		v2 = (u2*(m2 - m1) + 2*m1*u1 )/(m1+m2);
		//cout<<"V "<<v1<<","<<v2<<"\n\n";
		velocity_normal_component_1 = normal * v1;
		velocity_normal_component_2 = normal * v2;

		sphere1.set_velocity(velocity_normal_component_1 + velocity_tangent_component_1);
		sphere2.set_velocity(velocity_normal_component_2 + velocity_tangent_component_2);		
	}
	static void collision_response(Wall &wall, Sphere &sphere)
	{
		GLfloat center_plane_distance = sphere.get_radius();
		Vector3D normal(wall.a,wall.b,wall.c);
		normal.normalize();
		normal = normal * center_plane_distance;
		Point3D point_on_plane = sphere.get_center() + normal ;
		GLfloat point_subst_value = (float) wall.a * point_on_plane.x + wall.b * point_on_plane.y + wall.c * point_on_plane.z + wall.d;

		if(point_subst_value < 0.04 && point_subst_value  > -0.04)
			normal = Vector3D(0,0,0) - normal ;
		normal.normalize();
		Vector3D sphere_reversed_velocity = Vector3D(0,0,0) - sphere.get_velocity();

		Vector3D normal_component_velocity( normal * (sphere_reversed_velocity.x * normal.x +
											sphere_reversed_velocity.y * normal.y +
											sphere_reversed_velocity.z * normal.z ) );

		normal_component_velocity = normal_component_velocity *2;
		sphere.set_velocity( normal_component_velocity + sphere.get_velocity() );		
	}
};
